import { AICore } from "./AICore";
import path from 'path';
import { AIProperty, TypeProperty } from "./AIProperty";
import { chatChannels } from "./ChatChannels";
import { IatRecorder } from "./IatRecorder";
import { Thinkingdata } from "./Thinkingdata";

class WaitingMsg{
    txt:gui.TextField;
    lastTm=0;
    interval=500;
    dotnum=0;
    msg:string;
    constructor(item:gui.TextField,msg:string){
        this.txt=item;
        this.msg = msg;
        this.lastTm=Date.now();
    }   

    update(){
        let now = Date.now();
        if (now - this.lastTm > this.interval) {
            this.lastTm = now;
            this.dotnum++;
            if (this.dotnum > 5) {
                this.dotnum = 1;
            }
            let msg = this.msg + "....";
            for (let i = 0; i < this.dotnum; i++) {
                msg += ".";
            }
            this.txt.text = msg;
        }    
    } 
}

@IEditor.panel("aiPanel", {
    title: "AIPanel",
    icon: "editorResources/icons/panel/panel_ai.svg",
})


export class AIPanel extends IEditor.EditorPanel {

    /**麦克风状态是否没有声音以后自动发送，如果是这个状态，启动了麦克风也不会自动退出，除非用户点击麦克风退出 */
    static isRecorderAutoSend = false;

    cont: any;
    private _endItem: any;
    private msgID = 0;
    static ins: AIPanel;
    private _micMaxH: number;
    private _isEnableMic: boolean;
    private _micRecorder: IatRecorder;
    //聊天频道
    chatChannel: string;

    constructor() {
        super();
        Thinkingdata.ins.start();
    }

    async create(): Promise<void> {
        AIPanel.ins = this;
        this._panel = await gui.UIPackage.createWidget("editorResources/AIUI/AIPanel.widget");

        //this._panel.on("native_dragover", this.onDragOver, this);
        //this._panel.on("native_drop", this.onDrop, this);
        this._panel.on('right_click', (e:any) => {
            IEditor.Menu.getById("AIMenu").show();
            e.preventDefault();
            e.stopPropagation();
        });

        //this._panel = IEditorUI.UIPackage.createObject("aiplugin", "AIPanel");
        // this.setSize(this._panel.width, this._panel.height);
        // this._panel.addRelation(this, IEditorUI.RelationType.Size);
        // this.addChild(this._panel);
        this.cont = this._panel.getChild("cont");
        //(this.cont.element.firstElementChild as any).style['-webkit-user-select'] = "text";

        let input = this._panel.getChild("input");

        let AIChannel = this._panel.getChild('AIChannel') as gui.ComboBox;
        //console.log('cccc',chatChannels);
        let items = Object.keys(chatChannels);
        AIChannel.items = items;
        AIChannel.values = items;
        AIChannel.selectedIndex = 0;
        this.chatChannel = items[0];
        let tt = AIChannel.getChild('title');
        AIChannel.on('changed', (e: gui.Event) => {
            let v = (e.target as gui.ComboBox).value;
            this.chatChannel = v;
        }, null);


        let area: any = input.element.firstChild;
        area.setAttribute('rows', '1');
        area.style.resize = 'none';
        area.addEventListener('input', () => {
            this.checkInputWidth();
        });
        this._panel.on('size_changed', () => {
            this.checkContWidth();
            this.checkInputWidth();
            this.inputToEnd();
            this.contToEnd();
        });
        let mic = this._panel.getChild('mic');
        mic.touchable = true;
        mic.on("click", this.onClickMic, this);
        this.flushMicIco();


        let micImgGroup = this._panel.getChild('micImage');
        micImgGroup.on("click", this.onClickMic, this);

        this._micMaxH = this._panel.getChild("micRecordTip").height;
        input.on("key_up", (e: any) => {
            if ("Enter" == e.input.key && !e.input.ctrlKey) {
                let input = this._panel.getChild("input");
                input.text = "";
            }
        });

        input.on("key_down", (e: any) => {
            if ("Enter" == e.input.key && !e.input.ctrlKey) {
                this.enter();
                e.preventDefault();
                e.stopPropagation();
            }
        })
        //this.on("added_to_stage", this.addToStagehandle.bind(this));

        let enterBtn = this._panel.getChild("enterBtn");
        enterBtn.on('click', () => {
            this.enter();
        });

        //this.addMsg('', -2, { memory: false });


        /*
        this.addMsg({
            name: "aaa",
            properties: [
                {
                    name: "test",
                    inspector: GenTTSField,
                }
            ]
        },-1);

        let data: any = IEditor.DataWatcher.watch({});
        this.addMsg({
            name: "测试",
            properties: [
                {
                    name: "imgPath",
                    type: "string",
                    isAsset: true,
                    assetTypeFilter: 'Image',
                },
                {
                    name: "vec2",
                    type: "Vector2",
                    step: 1,
                },
                {
                    name: "vec3",
                    type: "Vector3",
                },
                {
                    name: "vec4",
                    type: "Vector4",
                },
                {
                    name: "参考图",
                    type: "string",
                    inspector: FileField,
                },
                {
                    name: "tipTest",
                    tip: "这里是一些提示文字，可以在这里写,这里也可以用[COLOR=#336699]UBB语法[/COLOR]",
                    inspector: TipField,
                },
                {
                    name: "textArea",
                    type: "string",
                    inspector: TextAreaField,
                },
                {
                    name: "test",
                    type: "number",
                    range: [0, 200],
                    step: 1,
                    onChange: (key: string, val: any, data: any) => {
                        console.log(key, val, data);
                    }
                },
                {
                    name: "test2",
                    type: "boolean",
                    default: true,
                },
                {
                    name: "测试",
                    type: "string",
                    hidden: 'data.test2',
                },
                {
                    name: "test3",
                    type: "number",
                    min: 0,
                    max: 100,
                    step: 1,
                },
                {
                    name: "test4",
                    type: 'number',
                    enumSource: [{ name: "low", value: 128 }, { name: "middle", value: 512 }, { name: "high", value: 1024 }],
                    default: 512,
                },
                {
                    name: "CustomButton",
                    inspector: CustomButtonsField,
                },
            ]
        }, -1, { data: data });
        setTimeout(() => {
            data.test3 = 10;
        }, 1000);
        */

        let aimenu = IEditor.Menu.create("AIMenu", [
            { label: "工具", id: "tools", submenu: [] },
            { label: "清空", click: () => { this.clear(); } },
        ]);
        AICore.inst.onPanelInited(this);

    }
    private onDragOver(evt: gui.Event) {
        var dt: DataTransfer = evt.data;
        dt.dropEffect = "copy";
        evt.preventDefault();
    }

    private async getItemString(item: DataTransferItem): Promise<string> {
        return new Promise<string>((res) => {
            item.getAsString(s => {
                res(s);
            });
        })
    }
    private async onDrop(evt: gui.Event) {
        let dt = (evt.data as DataTransfer);
        let item = dt.items[0];
        if (item.kind == 'string') {
            let s = await this.getItemString(item);
            if (s.startsWith(':')) {
                //这个是目录？
            }
            else {
                let fp: string;
                //文件
                if (s.startsWith('file:///')) {
                    fp = s.substring('file:///'.length);
                } else {
                    //具体的资源uuid
                    await AICore.inst.memory.addFile(s, 'user');
                    //展示出来
                    let asset = await Editor.assetDb.getAsset(s);
                    fp = Editor.assetDb.getFullPath(asset);
                }
                //temp
                let ext = path.extname(fp).toLowerCase();
                if (ext == '.png' || ext == '.jpg') {
                    //this.addImages([fp], null, []);
                } else if (ext == '.mp3' || ext == '.wav') {
                    this.addSound(fp);
                } else {
                    this.addMsg('File:' + fp, 1, { memory: false });
                }
            }
        } else if (item.kind == 'file') {
            let f = item.getAsFile();
            await AICore.inst.memory.addFile(f, 'user');
            //展示出来
            this.showFile(f);
        }
        console.log('onDrop', evt.data);
    }

    private async showFile(f: File) {
        let type = f.type;
        if (type.startsWith('audio')) {
            this.addSound((f as any).path);
        } else if (type.startsWith('image')) {
            //this.addImages([(f as any).path], null, []);
        } else {
            this.addMsg('File:' + (f as any).path, 1);
        }
    }

    private addToStagehandle() {
    }
    onStart?(): void {
        this.inputFocuse();
    }
    private flushMicIco() {
        let mic = this._panel.getChild("mic");
        if (this._isEnableMic) {
            //mic.icon = 'ui://rhyp5r0vepuk7';
            //mic.tooltips = '关闭麦克';
        } else {
            //mic.icon = 'ui://rhyp5r0vepuk7';
            //mic.tooltips = '启动麦克';
        }
        mic.visible = !this._isEnableMic;
    }
    private onClickMic() {
        this.isEnableMic = !this._isEnableMic;
        this.flushMicIco();
        if (this._isEnableMic) {
            if (!this._micRecorder) {
                this._micRecorder = new IatRecorder();
                this._micRecorder.onWillStatusChange = this.micWillStatusChange.bind(this);
                this._micRecorder.onTextChange = this.micTextChange.bind(this);
                this._micRecorder.updateWaveform = this.updateWaveform.bind(this);
            }
            this._micRecorder.start(AIPanel.isRecorderAutoSend);
        } else {
            if (this._micRecorder) {
                this._micRecorder.stop();
                this.enter();
            }
        }
    }
    private enter() {

        //  this.addImages([fpath.join(Editor.assetsPath, 'atlas/comp/image.png'), fpath.join(Editor.assetsPath, 'atlas/comp/image.png'), fpath.join(Editor.assetsPath, 'atlas/comp/image.png'), fpath.join(Editor.assetsPath, 'atlas/comp/image.png')], null, [{ label: "aaa", callback: null }, { label: "aaa", callback: null }, { label: "aaa", callback: null }, { label: "aaa", callback: null }], 2);
        //  return;
        //this.addSound(fpath.join(Editor.assetsPath, "sound/test.mp3"));
        // AIUtils.translation("你好啊，你能帮我翻译一下这段话么？").then((str) => {
        //     if (str) {
        //         console.log(str);
        //     } else {
        //         console.log("翻译错误，可能网络异常了");
        //     }
        // })
        //ViewImgScreen.ins.show(fpath.join(Editor.assetsPath, 'atlas/comp/image.png'));
        //this.addRange('aaaa', 0, 100, 30, null);
        //this.addImages([fpath.join(Editor.assetsPath, 'atlas/comp/image.png'), fpath.join(Editor.assetsPath, 'atlas/comp/image.png'), fpath.join(Editor.assetsPath, 'atlas/comp/image.png'), fpath.join(Editor.assetsPath, 'atlas/comp/image.png')], null);
        //this.addImages([fpath.join(Editor.assetsPath, 'atlas/comp/image.png'), fpath.join(Editor.assetsPath, 'atlas/comp/image.png'), fpath.join(Editor.assetsPath, 'atlas/comp/image.png'), fpath.join(Editor.assetsPath, 'atlas/comp/image.png')],null,[{label:"aaa",callback:null},{label:"aaa",callback:null},{label:"aaa",callback:null},{label:"aaa",callback:null}],2);

        let input = this._panel.getChild("input");
        let val = input.text.trim();
        if ("" != val) {
            if (!AIPanel.isRecorderAutoSend) {
                this._micRecorder && this._micRecorder.stop();
            }
            val = val.split("<").join("&lt;");
            val = val.split(">").join("&gt;");

            this.msgToAI(val);

            //this.addMsg(val);
        }
        input.text = "";
        this.checkInputWidth();
        this.inputFocuse();
    }

    inputFocuse(assetField?: any) {
        let input = this._panel.getChild("input");
        input.requestFocus();
        if (assetField) {
            //let objDesc = assetField.objType.$localizedCaption
            let objDesc = assetField.inspector.target.fileName;
            //let propDesc = assetField.property.$localizedCaption
            let propDesc = assetField.property.name;

            let texcolor = "#bbbbbb";
            let noticecolor = "#aaaaee";
            function _colorStr(color: string, str: string) {
                return `<span style="color:${color}">${str}</span>`
            }
            this.addMsg(_colorStr(texcolor, '任务：设置 ') + _colorStr(noticecolor, objDesc) + _colorStr(texcolor, ' 的 ') + _colorStr(noticecolor, propDesc) + _colorStr(texcolor, ' 属性'));

            let file = assetField.inspector.target.fileName + '_' + assetField.property.name + assetField.inspector.target.id;//"u_AlbedoTexture"
            file = file.replace(/\./g, '_');
            file += '.png';
            //AICore.inst.AI_Fill_AssetField(assetField, file);
            //这里可以通过AssetField来设置数据
            //assetField.target.setValue("***");
        }
    }
    msgToAI(msg: string) {
        this.addMsg(msg);
        let ai = AICore.inst
        let me = this;
        function reth() {
            let msgid: string = null;
            return async (str: string) => {
                if (msgid == null) {
                    msgid = await me.addMsg(str, -1);
                } else {
                    me.updateMsg(str, msgid);
                }
            }
        }
        ai.cmd(msg, reth());
    }
    clear() {
        this.cont.removeChildrenToPool();
    }

    private msgBindMem: { [key: string]: number } = {}


    async addPropertyMsg(property: TypeProperty | gui.Widget, data?: any, dir: 1 | -1 | -2 | gui.Widget = 1) {
        if (null == data) {
            data = {};
        }
        if (!IEditor.DataWatcher.isWatching(data))
            data = IEditor.DataWatcher.watch(data);

        let cont: gui.Widget;
        if (property instanceof gui.Widget) {
            cont = property;
        } else {
            cont = await AIProperty.create(property, data);
        }
        let item: gui.Widget;
        if ('number' == typeof dir) {
            item = await this.addItemFromDir(dir, cont);
        } else {
            item = dir;
            let title: gui.TextField = item.getChild("title");
            let bg = item.getChild("bg") as gui.Shape;
            if (title.visible) {
                title.visible = false;
                bg.removeRelation(title, gui.RelationType.Width);
                item.removeRelation(title, gui.RelationType.Height);
            }
            item.addChild(cont);
            cont.width = bg.width;
            cont.addRelation(bg, gui.RelationType.Width);
        }
        let list = cont.getChild("List") as gui.List;
        //依靠list的位置和大小来确定item的高度
        if (list) {
            item.height = list.height + list.y + 10;
            list.on('size_changed', () => {
                item.height = list.height + list.y + 10;
            });
        } else {
            //没有list则用cont本身。能不能不用list呢
            item.height = cont.height;
            cont.on('size_changed', () => {
                item.height = cont.height;
            });
        }
        (item as any).watchData = data;
        // this.checkIteWidth(item);
        // this.contToEnd();
        return item;
    }
    private async addItemFromDir(dir: number, cont?: any) {
        let item: any;
        if (1 != dir) {
            item = await gui.UIPackage.createWidget("editorResources/AIUI/YouMsg.widget");
        } else {
            item = await gui.UIPackage.createWidget("editorResources/AIUI/MeMsg.widget");
        }
        if (cont) {
            let title: gui.TextField = item.getChild("title");
            let bg = item.getChild("bg") as gui.Shape;
            bg.removeRelation(title, gui.RelationType.Width);
            item.removeRelation(title, gui.RelationType.Height);
            item.addChild(cont);
            title.visible = false;
            cont.width = bg.width;
            cont.addRelation(bg, gui.RelationType.Width);
        }
        let id = "msg_" + this.msgID;//原来写在第一句会有异步问题，多个msg得到相同的id
        this.msgID++;
        item.name = id;
        this.cont.addChild(item);
        this._endItem = item;
        return item;
    }

    async addMsg(msg: string | TypeProperty | gui.Widget, dir: 1 | -1 | -2 = 1, options?: { data?: any, memory?: boolean, editobjname?: string }) {
        let item: any = null;
        if ('object' == typeof msg) {
            let data = null;
            if (options) {
                data = options.data;
            }
            item = await this.addPropertyMsg(msg, data, dir);
        }

        if (null == item) {
            item = await this.addItemFromDir(dir);
            let tf: gui.TextField = item.getChild("title");
            tf.text = msg as string;
        }


        let id = item.name;
        let memory = true;
        if (options) {
            memory = options.memory ?? true;
        }
        this._endItem = item;
        //测试如果不延迟设置会导致TipField和下面控件的间距错误，暂时用这种方法处理
        // setTimeout(() => {
        //     if (null != item.parent) {
        //         this.checkIteWidth(item);
        //         this.contToEnd();
        //     }
        // }, 50);
        if (null != item.parent) {
            this.checkIteWidth(item);
            this.contToEnd();
        }

        //this.cont.element.scrollTop = this.cont.element.height;
        if ('string' == typeof msg) {
            let mem = AICore.inst.memory.addMessage(msg, dir > 0 ? 'user' : 'ai', options?.editobjname, memory);
            this.msgBindMem[id] = mem.id;
        }
        return id;
    }

    updateWaitingMsg(){
        for(let i=0,n=this._updateList.length; i<n; i++){
            this._updateList[i].update();
        }
    }

    onUpdate(){
        let n = this._updateList.length;
        if(n){
            this.updateWaitingMsg();
        }
    }

    private _updateList:WaitingMsg[]=[];
    async startWaitMsg(msg: string) {
        let item: any = null;
        item = await this.addItemFromDir(-1);
        let tf: gui.TextField = item.getChild("title");
        this._updateList.push(new WaitingMsg(tf,msg));
        tf.text = msg as string;

        let id = item.name;
        this._endItem = item;
        //测试如果不延迟设置会导致TipField和下面控件的间距错误，暂时用这种方法处理
        setTimeout(() => {
            if (null != item.parent) {
                this.checkIteWidth(item);
                this.contToEnd();
            }
        }, 50);
        return id;
    }    

    updateMsg(msg: string | TypeProperty | gui.Widget, id: string, data?: any) {
        let item = this.cont.getChild(id);
        if (!item) return;


        let widget = item.getChild("widget");
        if (widget) {
            item.removeChild(widget);
        }
        if ('string' == typeof msg) {
            let tf: gui.TextField = item.getChild("title");
            tf.visible = true;
            tf.text = msg;
        } else {
            this.addPropertyMsg(msg, data, item);
            this.checkIteWidth(item);
        }
    }

    getMsg(id: string) {
        let item = this.cont.getChild(id);
        if (!item) return null;
        let tf: gui.TextField = item.getChild("title");
        return tf.text;
    }

    removeMsg(id: string) {
        let item = this.cont.getChild(id);
        if (!item) return;
        let tf: gui.TextField = item.getChild("title");
        if(tf){
            let idx=-1;
            let find = this._updateList.find((v,i)=>{idx=i; return v.txt==tf;});
            if(find){
                this._updateList.splice(idx,1);
            }
        }
        this.cont.removeChild(item);
        //this.cont.itemPool.returnObject(item);
        AICore.inst.memory.removeMessage(this.msgBindMem[id]);
        delete this.msgBindMem[id];
    }

    addSound(path: string, autoplay = false) {
        let html = `<audio controls ${autoplay ? 'autoplay' : ''} >
            <source src="${path}" type="audio/mpeg">
        </audio>`;
        this.addMsg(html, -1);
    }

    private openImg(fullPath: string) {
        //ViewImgScreen.ins.show(fullPath);
    }
    private micTextChange(str: string) {
        let input = this._panel.getChild("input");
        input.text = str.trim();
        this.checkInputWidth();
        this.inputToEnd();
    }
    private updateWaveform(num: Float32Array) {
        let maxNum = 0;
        for (let i = num.length - 1; i >= 0; i--) {
            if (maxNum < num[i]) {
                maxNum = num[i];
            }
        }
        let bl = maxNum / 0.25;
        if (1 < bl) {
            bl = 1;
        }
        this._panel.getChild("micRecordTip").height = this._micMaxH * bl;
    }
    private micWillStatusChange(parStatus: string, status: string) {
        if ('end' == status) {
            if (!AIPanel.isRecorderAutoSend) {
                this.isEnableMic = false;
                this.flushMicIco();
            } else {
                this.enter();
            }
        }
    }
    private set isEnableMic(b: boolean) {
        this._isEnableMic = b;
        let micImgGroup = this._panel.getChild('micImgGroup');
        micImgGroup.visible = b;

    }
    private inputToEnd() {
        let input = this._panel.getChild("input");
        let area: any = input.element.firstChild;
        area.scrollTop = area.scrollHeight;
    }
    private checkContWidth() {
        for (let i = this.cont.numChildren - 1; i >= 0; i--) {
            this.checkIteWidth(this.cont.getChildAt(i));
        }
    }
    private checkInputWidth() {
        let input = this._panel.getChild("input");
        let area: any = input.element.firstChild;
        area.style.height = 'auto';
        let scrollHeight = area.scrollHeight;
        let maxH = this._panel.height * 0.3;
        if (33 > maxH) {
            maxH = 33;
        }
        if (33 >= scrollHeight) {
            scrollHeight = 19;
        } else if (maxH < scrollHeight) {
            scrollHeight = maxH;
        }
        area.style.height = `${scrollHeight}px`;
        if (input.height != scrollHeight) {
            input.height = scrollHeight;
            this.contToEnd();
        }
    }
    private contToEnd() {
        if (this._endItem && this._endItem.parent)
            //this.cont.scrollPane.scrollToView(this._endItem);
            this.cont.scroller.scrollTo(this._endItem);
    }

    onSelectionChanged() {
        AICore.inst.onSelectionChanged();
    }

    private checkIteWidth(item: gui.Widget) {
        if (!item) return;
        let widget = item.getChild("widget");
        let minW=0;
        if(widget&&(widget as any).minWidth){
            minW = (widget as any).minWidth;
        }
        let maxW = Math.max(minW, this.cont.width * 0.8);
        let tf: gui.TextField = item.getChild("title");
        if (tf.visible)
            tf.maxWrapWidth = maxW;

        if (widget) {
            let bg = item.getChild("bg");
            bg.width = maxW;
        }
    }
    @IEditor.onLoad
    static async init() {
        //await Editor.loadUIPackage("editorResources/aiplugin");
        Editor.typeRegistry.addTypes([{
            name: "Shader.TreeShader",
            base: "ShaderData",
            properties: [
                {
                    name: "u_ParamTexture",
                    type: "Texture2D",
                },
                {
                    name: "u_AlbedoTexture",
                    type: "Texture2D",
                },
                {
                    name: "u_DiffuseColor",
                    type: "Color",
                },
                {
                    name: "u_AlbedoIntensity",
                    type: "number",
                },
                {
                    name: "u_MaterialSpecular",
                    type: "Color",
                },
                {
                    name: "u_Shininess",
                    type: "number",
                },
                {
                    name: "u_TilingOffset",
                    type: "Vector4",
                },
                {
                    name: "u_Season",
                    type: "number",
                },
                {
                    name: "u_ParamWind",
                    type: "Vector4",
                },
                {
                    name: "u_ParamSize",
                    type: "number",
                },
                {
                    name: "u_ParamStride",
                    type: "number",
                },
                {
                    name: "u_TreeHeight",
                    type: "number",
                },
                {
                    name: "u_TreeCodeMax",
                    type: "number",
                },
                {
                    name: "u_BranchCodeMax",
                    type: "number",
                },
                {
                    name: "u_InstanceParam1",
                    type: "Vector4",
                },
                {
                    name: "u_InstanceParam2",
                    type: "Vector4",
                },
                {
                    name: "u_InstanceParam3",
                    type: "Vector4",
                },
            ]
        }]);
    }
}
